const F = 'fulfilled';
const R = 'rejected';
const P = 'pending';

class MyPromise {
  // fulfilled 值
  _value = void 0;
  // rejected 原因
  _reason = void 0;
  // 当前 Promise 状态
  _state = P;
  // then 方法所接收的 onFulfilled 回调
  _onFulFilledQueue = [];
  // then 方法所接收的 onRejected 回调
  _onRejectedQueue = [];

  constructor(resolver) {
    try {
      resolver(this._resolve, this._reject);
    } catch (e) {
      this._reject(e);
    }
  }

  static race(tasks) {
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < tasks.length; i++) {
        if (tasks[i] instanceof MyPromise) {
          // 依次把 resolve 传递给每个 MyPromise
          // 因为 Promise 状态一旦进入 fulfilled / rejected 状态就不可改变
          // 所以最先完成的 MyPromise 能够屏蔽掉后续的 MyPromise
          // 从而达到 race 的目的
          tasks[i].then(resolve, reject);
        } else {
          // 非 MyPromise 无需等待
          // 所以可以立即 resolve 并结束当前操作
          resolve(tasks[i]);
          break;
        }
      }
    });
  }

  static allSettled(tasks) {
    return new Promise(resolve => {
      const result = [];

      const addResolvedValue = (index, status, value) => {
        const payload = status === F ? {status, value} : {status, reason: value};
        result.splice(index, 0, payload);
        if (result.length === tasks.length) {
          resolve(result);
        }
      };

      for (let i = 0; i < tasks.length; i++) {
        if (tasks[i] instanceof MyPromise) {
          // 只要能够 resolved 都添加到 result 里
          tasks[i].then(
              value => addResolvedValue(i, F, value),
              reason => addResolvedValue(i, R, reason)
          );
        } else {
          addResolvedValue(i, F, tasks[i]);
        }
      }
    });
  }

  static all(tasks) {
    return new MyPromise((resolve, reject) => {
      const result = [];

      const addResolvedValue = (index, value) => {
        result.splice(index, 0, value);
        if (result.length === tasks.length) {
          resolve(result);
        }
      };

      for (let i = 0; i < tasks.length; i++) {
        if (tasks[i] instanceof MyPromise) {
          tasks[i].then(
              v => addResolvedValue(i, v),
              reject
          );
        } else {
          addResolvedValue(i, tasks[i]);
        }
      }
    });
  }

  static resolve(value) {
    return value instanceof MyPromise
        ? value
        : new MyPromise(resolve => resolve(value));
  }

  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason));
  }

  _resolve = (value) => {
    // 防止非 Pending 的状态发生变更
    if (this._state !== P) {
      return;
    }

    this._state = F;
    this._value = value;

    // 因为状态不可变更，所以可以清空无用的 rejected 回调
    this._onRejectedQueue = null;

    // 执行所有 fulfilled 回调
    consumeQueue(this._onFulFilledQueue);
  }

  _reject = (reason) => {
    // 防止非 Pending 的状态发生变更
    if (this._state !== P) {
      return;
    }

    this._state = R;
    this._reason = reason;

    // 因为状态不可变更，所以可以清空无用的 fulfilled 回调
    this._onFulFilledQueue = null;

    // 执行所有 rejected 回调
    consumeQueue(this._onRejectedQueue);
  }

  then(onFulfilled, onRejected) {
    const chainedPromise = new MyPromise((resolve, reject) => {
      // 利用闭包拿到上文的 chainedPromise 并把不同状态的 callback 独立地存储起来，以便于复用这段逻辑
      // 其中 resolvedValueGetter 以函数的形式延迟获得 value / reason，因为它们的结果可能是异步产生的而当前的代码却是同步执行的
      const createContextBoundCallback = (callback, resolvedValueGetter) => () => {
        queueMicrotask(() => {
          try {
            const chainedValue = callback(resolvedValueGetter());

            if (chainedPromise === chainedValue) {
              reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
              return;
            }

            resolveChainedPromise(chainedValue, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };

      onFulfilled = createContextBoundCallback(
          onFulfilled && isFunction(onFulfilled) ? onFulfilled : v => v,
          () => this._value);
      onRejected = createContextBoundCallback(
          onRejected && isFunction(onRejected) ? onRejected : reason => {
            throw reason
          },
          () => this._reason);

      switch (this._state) {
        case F:
          onFulfilled();
          break;
        case R:
          onRejected();
          break;
        default:
          // pending 状态时，不做任何处理
          this._onFulFilledQueue.push(onFulfilled);
          this._onRejectedQueue.push(onRejected);
          break;
      }
    });
    return chainedPromise;
  }

  catch(cb) {
    return this.then(null, cb);
  }

  finally(cb) {
    return this.then(
        // 忽略 finally 可能返回的值，把当前 resovled 值传递下去
        value => MyPromise.resolve(cb).then(() => value),
        // 如果 finally 回调没有异常，则把当前 rejected reason 传递下去
        reason => MyPromise.resolve(cb).then(() => {
          throw reason
        })
    )
  }
}

function consumeQueue(queue) {
  while (queue.length) {
    const cb = queue.shift();
    cb();
  }
}

function resolveChainedPromise(chainedValue, resolve, reject) {
  if (chainedValue instanceof MyPromise) {
    chainedValue.then(resolve, reject);
  } else {
    resolve(chainedValue);
  }
}

function isFunction(func) {
  return func && Object.prototype.toString.call(func) === '[object Function]';
}

module.exports = MyPromise;
